﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;

namespace Skywave.Net
{
    public class HttpWebRequestHelper
    {
        EventHandler _Download_ProgressChanged;
        public event EventHandler Download_ProgressChanged
        {
            add
            {
                _Download_ProgressChanged += value;
            }
            remove
            {
                _Download_ProgressChanged -= value;
            }
        }

        EventHandler<HttpWebRequestHelperDownloadCompletedEventArgs> _Download_Completed;
        public event EventHandler<HttpWebRequestHelperDownloadCompletedEventArgs> Download_Completed
        {
            add
            {
                _Download_Completed += value;
            }
            remove
            {
                _Download_Completed -= value;
            }
        }

        public int? Download_Length { get; set; }
        public int Download_Done { get; set; }

        /// <summary>
        /// Return calculated progress of download using length and done
        /// </summary>
        public double? Download_ProgressInPercent
        {
            get { return (Download_Length == null) ? null : (double?)((double)Download_Done / (double)Download_Length.Value) * 100d; }
        }

        public void Download<T>(string inUrl, int retryTimesOnException = 1, Threading.CancellationToken cancelToken = null)
        {
            try
            {
#if NETFX_CORE
                var thread1 = new System.Threading.Tasks.TaskFactory().StartNew((() =>
#else
                var thread1 = new System.Threading.Thread(new System.Threading.ThreadStart(() =>
#endif
                {
                    object r = null;
                    Exception r_Ex = null;
                    try
                    {
                        int retryTime_Counter;
                        retryTime_Counter = 1;
                        System.Threading.AutoResetEvent waitHandler = new System.Threading.AutoResetEvent(false);
                        while (retryTime_Counter <= retryTimesOnException)
                        {
                            Download_Done = 0;
                            Download_Length = null;
                            try
                            {
                                System.Net.WebRequest req_Header = System.Net.HttpWebRequest.Create(inUrl);
#if NETFX_CORE
#else
                                req_Header.Timeout = 5000;
#endif
                                req_Header.Method = "HEAD";
                                #region Cancel Check
                                if (cancelToken != null && cancelToken.IsCanceled)
                                {
                                    r = null;
                                    r_Ex = new OperationCanceledException();
                                    break;
                                }
                                #endregion
                                req_Header.BeginGetResponse((a1) =>
                                    {
                                        try
                                        {
                                            #region Cancel Check
                                            if (cancelToken != null && cancelToken.IsCanceled)
                                            {
                                                waitHandler.Set();
                                                return;
                                            }
                                            #endregion
                                            //
                                            using (System.Net.WebResponse resp_Header = req_Header.EndGetResponse(a1))
                                            {
                                                int resp_Header_ContentLength;
                                                if (int.TryParse(resp_Header.Headers["Content-Length"], out resp_Header_ContentLength))
                                                {
                                                    Download_Length = resp_Header_ContentLength;
                                                }
                                            }
                                            //
                                            #region Cancel Check
                                            if (cancelToken != null && cancelToken.IsCanceled)
                                            {
                                                waitHandler.Set();
                                                return;
                                            }
                                            #endregion
                                            //
                                            System.Net.HttpWebRequest hWReq = System.Net.WebRequest.Create(inUrl) as System.Net.HttpWebRequest;
#if NETFX_CORE
#else
                                            hWReq.Timeout = 10000;
#endif
                                            hWReq.Method = "GET";
                                            hWReq.Accept = null;
                                            //
                                            System.Net.WebResponse hWResp = null;
                                            hWReq.BeginGetResponse((a2) =>
                                                {
                                                    try
                                                    {
                                                        #region Cancel Check
                                                        if (cancelToken != null && cancelToken.IsCanceled)
                                                        {
                                                            waitHandler.Set();
                                                            return;
                                                        }
                                                        #endregion
                                                        //
                                                        hWResp = hWReq.EndGetResponse(a2);
                                                        // Read the response into a Stream object.
                                                        byte[] data_Native;
                                                        using (System.IO.MemoryStream ms1 = new System.IO.MemoryStream())
                                                        {
                                                            using (System.IO.Stream responseStream = hWResp.GetResponseStream())
                                                            {
                                                                int bufferSize = 1024;
                                                                byte[] buffer = new byte[bufferSize];
                                                                int bufferUsed;
                                                                do
                                                                {
                                                                    #region Cancel Check
                                                                    if (cancelToken != null && cancelToken.IsCanceled)
                                                                    {
                                                                        waitHandler.Set();
                                                                        return;
                                                                    }
                                                                    #endregion
                                                                    //
                                                                    bufferUsed = responseStream.Read(buffer, 0, bufferSize);
                                                                    if (bufferUsed > 0)
                                                                    {
                                                                        ms1.Write(buffer, 0, bufferUsed);
                                                                        Download_Done += bufferUsed;
                                                                        if (_Download_ProgressChanged != null)
                                                                            _Download_ProgressChanged(this, EventArgs.Empty);
                                                                    }
                                                                } while (bufferUsed > 0);
                                                            }
                                                            //
                                                            #region Cancel Check
                                                            if (cancelToken != null && cancelToken.IsCanceled)
                                                            {
                                                                waitHandler.Set();
                                                                return;
                                                            }
                                                            #endregion
                                                            //
                                                            data_Native = ms1.ToArray();
                                                            ms1.Position = 0;
                                                            //
                                                            if (typeof(T) == typeof(string))
                                                            {
                                                                string data;
                                                                using (var reader = new System.IO.StreamReader(ms1))
                                                                {
                                                                    data = reader.ReadToEnd();
                                                                }
                                                                r = data;
                                                            }
                                                            else if (typeof(T) == typeof(byte[]))
                                                            {
                                                                r = data_Native;
                                                            }
                                                            else if (typeof(T) == typeof(System.IO.MemoryStream))
                                                            {
                                                                System.IO.MemoryStream ms2 = new System.IO.MemoryStream(data_Native);
                                                                r = ms2;
                                                            }
                                                            //
                                                            waitHandler.Set();
                                                        }
                                                    }
                                                    catch (Exception ex3)
                                                    {
                                                        r = null;
                                                        r_Ex = ex3;
                                                        waitHandler.Set();
                                                    }
                                                }, null);
                                        }
                                        catch (Exception ex4)
                                        {
                                            r = null;
                                            r_Ex = ex4;
                                            waitHandler.Set();
                                        }
                                    }, null);
                            }
                            catch (Exception ex1)
                            {
                                r = null;
                                r_Ex = ex1;
                                waitHandler.Set();
                            }
                            //
                            waitHandler.WaitOne();
                            //
                            #region Cancel Check
                            if (cancelToken != null && cancelToken.IsCanceled)
                            {
                                r = null;
                                r_Ex = new OperationCanceledException();
                                break;
                            }
                            #endregion
                            //
                            if (r != null)
                            {
                                r_Ex = null;
                                var event_1 = _Download_Completed;
                                if (event_1 != null)
                                    event_1(this, new HttpWebRequestHelperDownloadCompletedEventArgs
                                    {
                                        IsSuccess = true,
                                        DownloadedData = r,
                                        Exception = null,
                                    });
                                break;
                            }
                            retryTime_Counter++;
                        }
                    }
                    catch (Exception ex_Main)
                    {
                        r = null;
                        r_Ex = ex_Main;
                    }
                    //
                    if (r == null)
                    {
                        var event_1 = _Download_Completed;
                        if (event_1 != null)
                            event_1(this, new HttpWebRequestHelperDownloadCompletedEventArgs
                            {
                                IsSuccess = false,
                                DownloadedData = r,
                                Exception = r_Ex,
                            });
                    }
                }));
#if !NETFX_CORE
                thread1.Start();
#endif
            }
            catch (Exception ex2)
            {
                var event_1 = _Download_Completed;
                if (event_1 != null)
                    event_1(this, new HttpWebRequestHelperDownloadCompletedEventArgs
                    {
                        IsSuccess = false,
                        DownloadedData = null,
                        Exception = ex2,
                    });
            }
        }
    }
}
